/*
    This file is part of LWX. 

    LWX is the abbreviation of the Light WiMax simulation program which 
    is a ns2 WiMAX network simulation module. LWX allows each user to 
    apply his/her designed scheduling algorithm or other mechanism used 
    in WiMAX network and generate the corresponding simulation results
    for further analysis or other non-commercial purposes.

    Copyright (C) 2008 Yen-Hung Cheng (pplong), NTUST in Taiwan, R.O.C.
    E-Mail: lwx.ns2@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _LWX_BIND_TCL
#define _LWX_BIND_TCL

int LWXClass::gen_rand_uniform(int low_bound, int up_bound)
{
  //  note:
  //  you need put the following code before using this function
  //  ps: only put these code one time
  //    srand(time(NULL)); // set seed
  //    rand();            // ignor the first rand (very high probability to get the same value)
  //
  
  int range = up_bound - low_bound +1;
  int ignor_num = RAND_MAX - RAND_MAX % range ; 

  while(true)
  {
    int rand_num = rand();
    if(rand_num < ignor_num)
    { 
      //cout << "rand: " << low_bound+rand_num%range << endl;
      return low_bound+rand_num%range;
    }else
    {
      continue;
    }
  }
}

vector <string> LWXClass::lwx_str_split(string str, string delim)
{
  vector <string> flow_info_vector;

  // spit string start
  int cutAt=0;

  while( (cutAt = str.find_first_of(delim)) != str.npos )
  {
    if(cutAt > 0)
    {
      flow_info_vector.push_back(str.substr(0,cutAt));
    }
    str = str.substr(cutAt+1);
  }
  if(str.length() > 0)
  {
    flow_info_vector.push_back(str);
  }
  return flow_info_vector;
}


void LWXClass::lwx_err_msg(int argc,const char* const* argv, string other_msg)
{
   string error_tcl_script= "Mac/LWX ";
   for(int i=0; i<argc;i++)
   {
     error_tcl_script.append(argv[i]);
     error_tcl_script.append(" ");
   }
  
   cout << endl;
   cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl ;
   cout << endl;
   cout << "  your lwx setting gets trouble, \n  please check your tcl script or look up lwx_bind_tcl.h" << endl;
   cout << endl;
   cout << "  error code:\n     " << error_tcl_script << endl;
   cout << "  note:  \n         " << other_msg << endl;
   cout << endl;
   cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl ;
   cout << endl;
   
   exit (1);
}

void LWXClass::bind()
{

  TclClass::bind();
  add_method("env");

}

int LWXClass::method(int ac,const char* const* av)
{

  Tcl& tcl=Tcl::instance();
  int argc=ac-2;
  const char* const* argv=av+2;

  if(argc==2)
  {
    if(strcmp(argv[1],"env")==0)
    {
      cout << "plz give me any hint!! 0rz " << endl;
    }
    return(TCL_OK);
  }
  /////////////////////////////
  // get informatoin start
  /////////////////////////////
  // Mac/LWX env get_node_info
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"get_node_info")==0)
  {
      
      Node * tnode = Node::nodehead_.lh_first;
      for (; tnode; tnode = tnode->nextnode()) 
      {
        cout << "node id : " << tnode->nodeid() << " mac: " << tnode->address()<< endl;
      }
      return (TCL_OK);
  }
  // Mac/LWX env get_node_info_lwx
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"get_node_info_lwx")==0)
  {
      
      for(unsigned int i=0;i<lwx_node_list.size();i++)
      {
        cout << "node id : " << lwx_node_list.at(i).node_id     << " "
             << "node mac: " << lwx_node_list.at(i).node_mac_addr   << " "
             << "node type: " << lwx_node_list.at(i).node_type  << " "
             << endl;
       
        if(lwx_node_list.at(i).node_type == NODE_TYPE_BS 
           || lwx_node_list.at(i).node_type == NODE_TYPE_RS)
        {
          cout << "control node list (id/type): " ;
        
          for(unsigned int j=0;j < lwx_node_list.at(i).ctrl_node.size();j++)
          {
            cout << lwx_node_list.at(i).ctrl_node.at(j).node_id ;
            cout << "/" << lwx_node_list.at(i).ctrl_node.at(j).node_type ;
            cout << ", ";
          }
          cout << "\n";
        }
       
      }
      return (TCL_OK);
  }
  // Mac/LWX env get_flow_info
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"get_flow_info")==0)
  {
    
    for(unsigned int i=0;i<lwx_node_list.size();i++)
    {
        cout << "node id : " << lwx_node_list.at(i).node_id     << " "
             << "node mac: " << lwx_node_list.at(i).node_mac_addr   << " "
             << "node type: " << lwx_node_list.at(i).node_type  << " "
             << endl;
       
        cout << "control flow list \n(src_id/from_id/to_id : \n" 
             << "src_ip/src_port/dst_ip/dst_port : \n"
             << "MCR_Avg/flow_type/QoS/Rmin/Rmax/Lmax/Jitter)" << endl;
        
        for(unsigned int j=0;j < lwx_node_list.at(i).ctrl_flow.size();j++)
        {
            
          cout << "(";
          cout << lwx_node_list.at(i).ctrl_flow.at(j).src_node_id << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).from_node_id << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).to_node_id << " : " ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).src_ip << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).src_port << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).dst_ip << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).dst_port << " : " ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).MCR_Avg << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).flow_type << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).QoS_Class << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).Rmin << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).Rmax << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).Lmax << "/" ;
          cout << lwx_node_list.at(i).ctrl_flow.at(j).Jitter << ")\n" ;
          
          
        }
        cout << "\n";
       
    }
    return (TCL_OK);
  }
  // get specific flow's relay link info list
  // Mac/LWX env get_relay_info $src_node_id $flow_info
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"get_relay_info")==0)
  {
      int src_node_id   = atoi(argv[3]);
      string flow_info  = argv[4];

      vector <string> flow_info_vector = lwx_str_split(flow_info,"/");
      
      int src_ip        = atoi(flow_info_vector.at(0).c_str());
      int src_port      = atoi(flow_info_vector.at(1).c_str());
      int dst_ip        = atoi(flow_info_vector.at(2).c_str());
      int dst_port      = atoi(flow_info_vector.at(3).c_str());
      
      int node_index    = -1;
      int flow_index    = -1;
 
      // find node index in node_list
      for(unsigned int i=0 ;i <lwx_node_list.size();i++)
      {
        if (lwx_node_list.at(i).node_id == src_node_id)
        {
          node_index = i;
          break;
        }
      }

      // if can't find this node id, return error
      if(node_index == -1)
      {
        lwx_err_msg(argc, argv, "can't find the node id, in setting relay flow");
        
      }

      // find flow index in this node
      LWX_Node *tmp_node = &lwx_node_list.at(node_index);
      for(unsigned int i=0 ;i <tmp_node->ctrl_flow.size();i++)
      {
        if (tmp_node->ctrl_flow.at(i).src_ip == src_ip &&
            tmp_node->ctrl_flow.at(i).src_port == src_port &&
            tmp_node->ctrl_flow.at(i).dst_ip == dst_ip &&
            tmp_node->ctrl_flow.at(i).dst_port == dst_port)
        {
          flow_index=i;
          break;
        }
      }
      
      // if can't find this flow, return error
      if(flow_index == -1)
      {
        lwx_err_msg(argc, argv, "can't find the flow, in setting relay flow");
        
      }
      
      // get relay info list of this flow
      Flow_Queue *tmp_flow = &tmp_node->ctrl_flow.at(flow_index);
      
      ostringstream tmp_relay_info;
      tmp_relay_info << "src_nid: " << src_node_id << " flow: " << flow_info 
                     << " relay info list: (rs_nid/flow_type/MCR_Avg) " << endl;
      
      for(unsigned int i=0;i<tmp_flow->relay_link.size();i++)
      {
        tmp_relay_info << "(" << tmp_flow->relay_link.at(i).rs_node_id 
                       << "/" << tmp_flow->relay_link.at(i).flow_type
                       << "/" << tmp_flow->relay_link.at(i).MCR_Avg 
                       << ") ";
      } 
      
      cout << tmp_relay_info.str() << endl;
      return (TCL_OK);
      
      
  }
  // show flow attribute for src node
  // Mac/LWX env get_get_flow_var_info $src_nid $from_nid $to_nid $flow_info <type>
  if(argc==8 && strcmp(argv[1],"env")==0 && strcmp(argv[2],"get_flow_var_info")==0 )
  {
    int src_node_id   = atoi(argv[3]);
    int from_node_id  = atoi(argv[4]);
    int to_node_id    = atoi(argv[5]);
    string flow_info  = argv[6];
    vector <string> flow_info_vector = lwx_str_split(flow_info,"/");
      
    int src_ip        = atoi(flow_info_vector.at(0).c_str());
    int src_port      = atoi(flow_info_vector.at(1).c_str());
    int dst_ip        = atoi(flow_info_vector.at(2).c_str());
    int dst_port      = atoi(flow_info_vector.at(3).c_str());
      
    int node_index    = -1;
    int flow_index    = -1;
 
    // find node index in node_list
    for(unsigned int i=0 ;i <lwx_node_list.size();i++)
    {
      if (lwx_node_list.at(i).node_id == from_node_id)
      {
        node_index = i;
        break;
      }
    }

    // if can't find this node id, return error
    if(node_index == -1)
    {
      lwx_err_msg(argc, argv, "can't find the node id, in get_flow_info");
    }

    // find flow index in this node
    LWX_Node *tmp_node = &lwx_node_list.at(node_index);
    for(unsigned int i=0 ;i <tmp_node->ctrl_flow.size();i++)
    {
      if (tmp_node->ctrl_flow.at(i).src_node_id == src_node_id &&
          tmp_node->ctrl_flow.at(i).from_node_id == from_node_id &&
          tmp_node->ctrl_flow.at(i).to_node_id == to_node_id &&
          tmp_node->ctrl_flow.at(i).src_ip == src_ip &&
          tmp_node->ctrl_flow.at(i).src_port == src_port &&
          tmp_node->ctrl_flow.at(i).dst_ip == dst_ip &&
          tmp_node->ctrl_flow.at(i).dst_port == dst_port
         )
      {
        flow_index=i;
        break;
      }
    }
      
    // if can't find this flow, return error
    if(flow_index == -1)
    {
      lwx_err_msg(argc, argv, "can't find the flow, in get_flow_info");
        
    }
      
    // show flow attribute
    Flow_Queue *tmp_flow = &tmp_node->ctrl_flow.at(flow_index);
    if(strcmp(argv[7],"MCR_Subch")==0)
    {
      int num_of_subch = 0 ; 
      num_of_subch = tmp_flow->MCR_Subch.size();
        
      cout << "get_flow_info Subch_num " << "src_node_id/from_node_id/to_node_id flow_info MCR_Avg" << endl;
      cout << src_node_id << "/" << from_node_id << "/" << to_node_id << " " << flow_info << " " << tmp_flow->MCR_Avg<< endl;
      for(int i=0;i<num_of_subch;i++)
      {
        cout << i << " : " << tmp_flow->MCR_Subch.at(i) << endl;
      }
    }
    return (TCL_OK);
  }
  /////////////////////////////
  // get informatoin end
  /////////////////////////////
  
  /////////////////////////////
  // set or get env parameters start
  /////////////////////////////
  
  // show burst / map msg
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"Show_DL_Access_MAP")==0)
  {
     Show_DL_Access_MAP=atoi(argv[3]);
     cout << "Show_DL_Access_MAP: " << Show_DL_Access_MAP << endl;
     return (TCL_OK);
  }
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"Show_DL_Access_Burst")==0)
  {
     Show_DL_Access_Burst=atoi(argv[3]);
     cout << "Show_DL_Access_Burst: " << Show_DL_Access_Burst << endl;
     return (TCL_OK);
  }
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"Show_DL_Relay_MAP")==0)
  {
     Show_DL_Relay_MAP=atoi(argv[3]);
     cout << "Show_DL_Relay_MAP: " << Show_DL_Relay_MAP << endl;
     return (TCL_OK);
  }
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"Show_DL_Relay_Burst")==0)
  {
     Show_DL_Relay_Burst=atoi(argv[3]);
     cout << "Show_DL_Relay_Burst: " << Show_DL_Relay_Burst << endl;
     return (TCL_OK);
  }
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"Show_UL_Access_MAP")==0)
  {
     Show_UL_Access_MAP=atoi(argv[3]);
     cout << "Show_UL_Access_MAP: " << Show_UL_Access_MAP << endl;
     return (TCL_OK);
  }
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"Show_UL_Access_Burst")==0)
  {
     Show_UL_Access_Burst=atoi(argv[3]);
     cout << "Show_UL_Access_Burst: " << Show_UL_Access_Burst << endl;
     return (TCL_OK);
  }
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"Show_UL_Relay_MAP")==0)
  {
     Show_UL_Relay_MAP=atoi(argv[3]);
     cout << "Show_UL_Relay_MAP: " << Show_UL_Relay_MAP << endl;
     return (TCL_OK);
  }
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"Show_UL_Relay_Burst")==0)
  {
     Show_UL_Relay_Burst=atoi(argv[3]);
     cout << "Show_UL_Relay_Burst: " << Show_UL_Relay_Burst << endl;
     return (TCL_OK);
  }
  // set bandwidth allocation type
  // Mac/LWX env BWA_Algorithm_Type   1
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"BWA_Scheduler_Algo_Type")==0)
  {
     BWA_Scheduler_Algo_Type=atoi(argv[3]);
     cout << "BWA_Scheduler_Algo_Type: " << BWA_Scheduler_Algo_Type << endl;
     return (TCL_OK);
  }
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"BWA_Constructor_Algo_Type")==0)
  {
     BWA_Constructor_Algo_Type=atoi(argv[3]);
     cout << "BWA_Constructor_Algo_Type: " << BWA_Constructor_Algo_Type << endl;
     return (TCL_OK);
  }
  // adding BS node
  // Mac/LWX env add_bs nid $node_id(0)
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"add_bs")==0 
     && strcmp(argv[3],"node_id")==0)
  {
      Node * tnode = Node::nodehead_.lh_first;
      LWX_Node tmp_node;
      LWX_Node_Info tmp_node_info;
      int node_id = atoi(argv[4]);
      
      // adding node
      tmp_node.node_id   = node_id;
      for (; tnode; tnode = tnode->nextnode()) 
      {
        if(tnode->nodeid()==tmp_node.node_id)
        {
          tmp_node.node_mac_addr = tnode->address() ;
          break;
        }
      }
      tmp_node.node_type = NODE_TYPE_BS ;
      tmp_node_info.node_id=tmp_node.node_id;
      tmp_node_info.node_type = tmp_node.node_type ;
      tmp_node.ctrl_node.push_back(tmp_node_info);
      lwx_node_list.push_back(tmp_node);
      
      return (TCL_OK);
  }
  // adding SS node
  // Mac/LWX env add_ss nid $node_id(1) bs_nid $node_id(0)
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"add_ss")==0
     && strcmp(argv[3],"node_id")==0 && strcmp(argv[5],"bs_node_id")==0 )
  {
      
      Node * tnode = Node::nodehead_.lh_first;
      LWX_Node tmp_node;
      LWX_Node_Info tmp_node_info;
      int bs_node_id;
      
      // adding node
      tmp_node.node_id   = atoi(argv[4]);
      for (; tnode; tnode = tnode->nextnode()) 
      {
        if(tnode->nodeid()==tmp_node.node_id)
        {
          tmp_node.node_mac_addr = tnode->address() ;
          break;
        }
      }
      tmp_node.node_type = NODE_TYPE_SS ;
      lwx_node_list.push_back(tmp_node);
      
      // adding ss to its bs's node_list
      bs_node_id=atoi(argv[6]);
      for(unsigned int i=0;i<lwx_node_list.size();i++)
      {
        if(lwx_node_list.at(i).node_id == bs_node_id)
        {
          tmp_node_info.node_id=tmp_node.node_id;
          tmp_node_info.node_type = tmp_node.node_type ;
          lwx_node_list.at(i).ctrl_node.push_back(tmp_node_info);
        }
      }
      return (TCL_OK);
  }
  // adding RS node
  // Mac/LWX env add_rs nid $node_id(1) bs_nid $node_id(0)
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"add_rs")==0
       && strcmp(argv[3],"node_id")==0 && strcmp(argv[5],"bs_node_id")==0 )
  {
      
      Node * tnode = Node::nodehead_.lh_first;
      LWX_Node tmp_node;
      LWX_Node_Info tmp_node_info;
      int bs_node_id;
      
      // adding node
      tmp_node.node_id   = atoi(argv[4]);
      for (; tnode; tnode = tnode->nextnode()) 
      {
        if(tnode->nodeid()==tmp_node.node_id)
        {
          tmp_node.node_mac_addr = tnode->address() ;
          break;
        }
      }
      tmp_node.node_type = NODE_TYPE_RS ;
      lwx_node_list.push_back(tmp_node);
      
      // adding rs to its bs's node_list
      bs_node_id=atoi(argv[6]);
      for(unsigned int i=0;i<lwx_node_list.size();i++)
      {
        if(lwx_node_list.at(i).node_id == bs_node_id)
        {
          tmp_node_info.node_id=tmp_node.node_id;
          tmp_node_info.node_type = tmp_node.node_type ;
          lwx_node_list.at(i).ctrl_node.push_back(tmp_node_info);
        }
      }
      return (TCL_OK);
  }
  // Attach ss to RS 
  // Mac/LWX env attach_rs nid $node_id(1) rs_nid $node_id(0)
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"attach_rs")==0 
       && strcmp(argv[3],"node_id")==0 && strcmp(argv[5],"rs_node_id")==0 )
  {
      
      Node * tnode = Node::nodehead_.lh_first;
      LWX_Node_Info tmp_node_info;
      int bs_node_id;
      
      
      // adding ss to its bs's node_list
      bs_node_id=atoi(argv[6]);
      for(unsigned int i=0;i<lwx_node_list.size();i++)
      {
        if(lwx_node_list.at(i).node_id == bs_node_id)
        {
          tmp_node_info.node_id   = atoi(argv[4]);
          tmp_node_info.node_type = NODE_TYPE_SS  ;
          lwx_node_list.at(i).ctrl_node.push_back(tmp_node_info);
        }
      }
      return (TCL_OK);
  }
  // adding flow
  // Mac/LWX env add_flow <type> $src_nid $from_nid $to_nid $flow_info
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"add_flow")==0)
  {

      int src_node_id   = atoi(argv[4]);
      int from_node_id  = atoi(argv[5]);
      int to_node_id    = atoi(argv[6]);
      string flow_info  = argv[7];
      vector <string> flow_info_vector = lwx_str_split(flow_info,"/");
      
      int src_ip        = atoi(flow_info_vector.at(0).c_str());
      int src_port      = atoi(flow_info_vector.at(1).c_str());
      int dst_ip        = atoi(flow_info_vector.at(2).c_str());
      int dst_port      = atoi(flow_info_vector.at(3).c_str());
      
      int node_index    = -1;
 
      // find node index in node_list
      for(unsigned int i=0 ;i <lwx_node_list.size();i++)
      {
        if (lwx_node_list.at(i).node_id == from_node_id)
        {
          node_index = i;
          break;
        }
      }
      
      // if can't find this node id, return error
      if(node_index == -1)
      {
        lwx_err_msg(argc, argv, "can't find this node id, in adding flow");
      }
      
      // set flow_queue attributes
      Flow_Queue new_flow_queue;
      
      new_flow_queue.src_node_id  = src_node_id;
      new_flow_queue.from_node_id = from_node_id;
      new_flow_queue.to_node_id   = to_node_id;
      new_flow_queue.src_ip       = src_ip;
      new_flow_queue.src_port     = src_port;
      new_flow_queue.dst_ip       = dst_ip;
      new_flow_queue.dst_port     = dst_port;
      
      if( strcmp(argv[3],"bs_to_ss")==0 )
      {
        new_flow_queue.flow_type    = FLOW_TYPE_BS_TO_SS;
      }
      else if( strcmp(argv[3],"ss_to_bs")==0 )
      {
        new_flow_queue.flow_type    = FLOW_TYPE_SS_TO_BS;
      }
      else if( strcmp(argv[3],"rs_to_ss_tp")==0 )
      {
        new_flow_queue.flow_type    = FLOW_TYPE_RS_TO_SS_TP;
      }
      else if( strcmp(argv[3],"rs_to_ss_nontp")==0 )
      {
        new_flow_queue.flow_type    = FLOW_TYPE_RS_TO_SS_NONTP;
      }
      else if( strcmp(argv[3],"rs_to_bs")==0 )
      {
        new_flow_queue.flow_type    = FLOW_TYPE_RS_TO_BS;
      }
      else if( strcmp(argv[3],"ss_to_rs_tp")==0 ||
               strcmp(argv[3],"ss_to_rs_nontp")==0 ||
               strcmp(argv[3],"bs_to_rs")==0 )
      {
        // should be set_relay not add_flow
        // but this desgin may lead some problems or mis-understanding
        lwx_err_msg(argc, argv , "error when adding flow in tcl, this flow type is not allowed");
      }
      else
      {
        lwx_err_msg(argc, argv , "error when adding flow in tcl, wrong flow type.");
      }
      
      // adding flow_queue into bs node flow queue
      lwx_node_list.at(node_index).ctrl_flow.push_back(new_flow_queue);
      return (TCL_OK);
  }
  // set flow attribute for src node
  // Mac/LWX env set_flow $src_nid $from_nid $to_nid $flow_info <type> <value>
  if(strcmp(argv[1],"env")==0 && strcmp(argv[2],"set_flow")==0 )
  {
      int src_node_id   = atoi(argv[3]);
      int from_node_id  = atoi(argv[4]);
      int to_node_id    = atoi(argv[5]);
      string flow_info  = argv[6];
      vector <string> flow_info_vector = lwx_str_split(flow_info,"/");
      
      int src_ip        = atoi(flow_info_vector.at(0).c_str());
      int src_port      = atoi(flow_info_vector.at(1).c_str());
      int dst_ip        = atoi(flow_info_vector.at(2).c_str());
      int dst_port      = atoi(flow_info_vector.at(3).c_str());
      
      int node_index    = -1;
      int flow_index    = -1;
 
      // find node index in node_list
      for(unsigned int i=0 ;i <lwx_node_list.size();i++)
      {
        if (lwx_node_list.at(i).node_id == from_node_id)
        {
          node_index = i;
          break;
        }
      }

      // if can't find this node id, return error
      if(node_index == -1)
      {
        lwx_err_msg(argc, argv, "can't find the node id, in setting flow");
      }

      // find flow index in this node
      LWX_Node *tmp_node = &lwx_node_list.at(node_index);
      for(unsigned int i=0 ;i <tmp_node->ctrl_flow.size();i++)
      {
        if (tmp_node->ctrl_flow.at(i).src_node_id == src_node_id &&
            tmp_node->ctrl_flow.at(i).from_node_id == from_node_id &&
            tmp_node->ctrl_flow.at(i).to_node_id == to_node_id &&
            tmp_node->ctrl_flow.at(i).src_ip == src_ip &&
            tmp_node->ctrl_flow.at(i).src_port == src_port &&
            tmp_node->ctrl_flow.at(i).dst_ip == dst_ip &&
            tmp_node->ctrl_flow.at(i).dst_port == dst_port
           )
        {
          flow_index=i;
          break;
        }
      }
      
      // if can't find this flow, return error
      if(flow_index == -1)
      {
        lwx_err_msg(argc, argv, "can't find the flow, in setting flow");
        
      }
      
      // set flow attribute
      Flow_Queue *tmp_flow = &tmp_node->ctrl_flow.at(flow_index);
      if(strcmp(argv[7],"MCR_Subch")==0)
      {
        int num_of_subch = 0 ; 
        
        // downlink
        if( tmp_flow->flow_type == FLOW_TYPE_BS_TO_SS ||
            tmp_flow->flow_type == FLOW_TYPE_BS_TO_RS ||
            tmp_flow->flow_type == FLOW_TYPE_RS_TO_SS_TP ||
            tmp_flow->flow_type == FLOW_TYPE_RS_TO_SS_NONTP)
        {
          num_of_subch = DL_PUSC_SUBCH_NUM ;
        }
        // uplink
        else if ( tmp_flow->flow_type == FLOW_TYPE_SS_TO_BS ||
                  tmp_flow->flow_type == FLOW_TYPE_RS_TO_BS ||
                  tmp_flow->flow_type == FLOW_TYPE_SS_TO_RS_TP ||
                  tmp_flow->flow_type == FLOW_TYPE_SS_TO_RS_NONTP)
        {
          num_of_subch = UL_PUSC_SUBCH_NUM ;
        }
        else
        {
          lwx_err_msg(argc, argv , "error when set flow MCR_Subch in tcl, wrong flow type.");
        }
          
        // if MCR_Subch isn't init, init here
        if(tmp_flow->MCR_Subch.empty())
        {
         
          // initial MCR_Subch
          tmp_flow->MCR_Subch.resize(num_of_subch,0);
          
        }
        
        // automatic gen MCR_Subch by random (uniform)
        // Mac/LWX env set_flow $src_nid $from_nid $to_nid $flow_info MCR_Subch Auto <MCR1>/<MCR2>/<MCR3>/...
        if(strcmp(argv[8],"Auto")==0)
        {
          string MCR_list_str  = argv[9];
          vector <double> MCR_list_vector;
          vector <string> MCR_list_str_vector = lwx_str_split(MCR_list_str,"/");
          int MCR_list_size = MCR_list_str_vector.size();
        
          MCR_list_vector.resize(MCR_list_size);
          
          for(int i=0;i<MCR_list_size;i++)
          {
            istringstream(MCR_list_str_vector.at(i)) >> MCR_list_vector.at(i);
            if(MCR_list_vector.at(i) < 0)
            {
              lwx_err_msg(argc, argv , 
                "error when setting flow in tcl, MCR_Subch should not be smaller than zero.");
            }
          }
          
          // auto set MCR of each subch
          for(int i=0;i<num_of_subch;i++)
          {
            tmp_flow->MCR_Subch.at(i) = MCR_list_vector.at(gen_rand_uniform(0, MCR_list_size-1));
          }
          
          // calculate MCR_Avg and set to flow's MCR_Avg var
          double avg_mcr_tmp=0;
          for(int i=0;i<num_of_subch;i++)
          {
            avg_mcr_tmp+=tmp_flow->MCR_Subch.at(i);
          }
          tmp_flow->MCR_Avg = avg_mcr_tmp/num_of_subch;
        }
        // Mac/LWX env set_flow $src_nid $from_nid $to_nid $flow_info MCR_Subch <Subch Num> <MCR>
        // the unit of MCR is (bytes / (subch.*symbol))
        else
        {
          int subch_num_tmp =0;
          double MCR_tmp = 0;
          istringstream(argv[8]) >> subch_num_tmp;
          istringstream(argv[9]) >> MCR_tmp;
          
          if(MCR_tmp <0)
          {
            lwx_err_msg(argc, argv , 
              "error when setting flow in tcl, MCR_Subch should not be smaller than zero.");
          }
          
          tmp_flow->MCR_Subch.at(subch_num_tmp) = MCR_tmp;
          
          // calculate MCR_Avg and set to flow's MCR_Avg var
          double avg_mcr_tmp=0;
          for(int i=0;i<num_of_subch;i++)
          {
            avg_mcr_tmp+=tmp_flow->MCR_Subch.at(i);
          }
          tmp_flow->MCR_Avg = avg_mcr_tmp/num_of_subch;
        }
        // customize MCR_Subch
        
        
      }
      else if(strcmp(argv[7],"QoS_Class")==0)
      {
        istringstream(argv[8]) >> tmp_flow->QoS_Class;
      }
      else if(strcmp(argv[7],"Rmin")==0)
      {
        istringstream(argv[8]) >> tmp_flow->Rmin;
      }
      else if(strcmp(argv[7],"Rmax")==0)
      {
        istringstream(argv[8]) >> tmp_flow->Rmax;
      }
      else if(strcmp(argv[7],"Lmax")==0)
      {
        istringstream(argv[8]) >> tmp_flow->Lmax;
      }
      else if(strcmp(argv[7],"Jitter")==0)
      {
        istringstream(argv[8]) >> tmp_flow->Jitter;
      }
      else
      {
        lwx_err_msg(argc, argv , "error when setting flow in tcl, wrong flow attribute.");
        
      }
      return (TCL_OK);
    
  }
  // add flow's first relay link (only first hop) and set relative relay link attribution
  // only for ss_to_rs_tp, ss_to_rs_nontp, bs_to_rs
  // Mac/LWX env set_relay <type> $src_nid $flow_info $rs_nid MCR_Avg $relay_MCR
  if(strcmp(argv[1],"env")==0 &&  strcmp(argv[2],"set_relay")==0  &&
     ( strcmp(argv[3],"ss_to_rs_tp")==0 ||  strcmp(argv[3],"ss_to_rs_nontp")==0 || 
        strcmp(argv[3],"bs_to_rs")==0 ) )
  {
      string flow_type  = argv[3];
      int src_node_id   = atoi(argv[4]);
      string flow_info  = argv[5];
      int rs_node_id    = atoi(argv[6]);
      
      vector <string> flow_info_vector = lwx_str_split(flow_info,"/");
      
      int src_ip        = atoi(flow_info_vector.at(0).c_str());
      int src_port      = atoi(flow_info_vector.at(1).c_str());
      int dst_ip        = atoi(flow_info_vector.at(2).c_str());
      int dst_port      = atoi(flow_info_vector.at(3).c_str());
      
      int node_index    = -1;
      int rs_node_index = -1;
      int flow_index    = -1;
 
      // find node index in node_list
      for(unsigned int i=0 ;i <lwx_node_list.size();i++)
      {
        if (lwx_node_list.at(i).node_id == src_node_id)
        {
          node_index = i;
          break;
        }
      }

      // if can't find this node id, return error
      if(node_index == -1)
      {
        lwx_err_msg(argc, argv, "can't find the node id, in setting relay flow");
        
      }
      
      // check rs node id is correct
      for(unsigned int i=0 ;i <lwx_node_list.size();i++)
      {
        if (lwx_node_list.at(i).node_id == rs_node_id && 
            lwx_node_list.at(i).node_type == NODE_TYPE_RS)
        {
          rs_node_index = i;
          break;
        }
      }

      // if can't find this node id, return error
      if(rs_node_index == -1)
      {
        lwx_err_msg(argc, argv, "can't find the rs node id, in setting relay flow");
        
      }
      
      

      // find flow index in this node
      LWX_Node *tmp_node = &lwx_node_list.at(node_index);
      for(unsigned int i=0 ;i <tmp_node->ctrl_flow.size();i++)
      {
        if (tmp_node->ctrl_flow.at(i).src_ip == src_ip &&
            tmp_node->ctrl_flow.at(i).src_port == src_port &&
            tmp_node->ctrl_flow.at(i).dst_ip == dst_ip &&
            tmp_node->ctrl_flow.at(i).dst_port == dst_port)
        {
          flow_index=i;
          break;
        }
      }
      
      // if can't find this flow, return error
      if(flow_index == -1)
      {
        lwx_err_msg(argc, argv, "can't find the flow, in setting relay flow");
        
      }
      
      // creat a new LWX_Relay_Link_Info object
      LWX_Relay_Link_Info tmp_relay_link_info;
      tmp_relay_link_info.rs_node_id = rs_node_id;
      
      if(flow_type=="ss_to_rs_tp")
      {
        tmp_relay_link_info.flow_type = FLOW_TYPE_SS_TO_RS_TP;
      }
      else if(flow_type=="ss_to_rs_nontp")
      {
        tmp_relay_link_info.flow_type = FLOW_TYPE_SS_TO_RS_NONTP;
      }
      else if(flow_type=="bs_to_rs")
      {
        tmp_relay_link_info.flow_type = FLOW_TYPE_BS_TO_RS;
      }
      else
      {
        lwx_err_msg(argc, argv, "can't find the flow, in setting flow");
        
      }

      if(strcmp(argv[7],"MCR_Subch")==0)
      {
        int num_of_subch = 0 ; 
        
        // downlink
        if( tmp_relay_link_info.flow_type == FLOW_TYPE_BS_TO_SS ||
            tmp_relay_link_info.flow_type == FLOW_TYPE_BS_TO_RS ||
            tmp_relay_link_info.flow_type == FLOW_TYPE_RS_TO_SS_TP ||
            tmp_relay_link_info.flow_type == FLOW_TYPE_RS_TO_SS_NONTP)
        {
          num_of_subch = DL_PUSC_SUBCH_NUM ;
        }
        // uplink
        else if ( tmp_relay_link_info.flow_type == FLOW_TYPE_SS_TO_BS ||
                  tmp_relay_link_info.flow_type == FLOW_TYPE_RS_TO_BS ||
                  tmp_relay_link_info.flow_type == FLOW_TYPE_SS_TO_RS_TP ||
                  tmp_relay_link_info.flow_type == FLOW_TYPE_SS_TO_RS_NONTP)
        {
          num_of_subch = UL_PUSC_SUBCH_NUM ;
        }
        else
        {
          lwx_err_msg(argc, argv , "error when set relay flow MCR_Subch in tcl, wrong flow type.");
        }
          
        // if MCR_Subch isn't init, init here
        if(tmp_relay_link_info.MCR_Subch.empty())
        {
         
          // initial MCR_Subch
          tmp_relay_link_info.MCR_Subch.resize(num_of_subch,0);
          
        }
        
        // automatic gen MCR_Subch by random (uniform)
        // Mac/LWX env set_flow $src_nid $from_nid $to_nid $flow_info MCR_Subch Auto <MCR1>/<MCR2>/<MCR3>/...
        if(strcmp(argv[8],"Auto")==0)
        {
          string MCR_list_str  = argv[9];
          vector <double> MCR_list_vector;
          vector <string> MCR_list_str_vector = lwx_str_split(MCR_list_str,"/");
          int MCR_list_size = MCR_list_str_vector.size();
        
          MCR_list_vector.resize(MCR_list_size);
          
          for(int i=0;i<MCR_list_size;i++)
          {
            istringstream(MCR_list_str_vector.at(i)) >> MCR_list_vector.at(i);
            if(MCR_list_vector.at(i) < 0)
            {
              lwx_err_msg(argc, argv , 
                "error when setting relay flow in tcl, MCR_Subch should not be smaller than zero.");
            }
          }
          
          // auto set MCR of each subch
          for(int i=0;i<num_of_subch;i++)
          {
            tmp_relay_link_info.MCR_Subch.at(i) = MCR_list_vector.at(gen_rand_uniform(0, MCR_list_size-1));
          }
          
          // calculate MCR_Avg and set to flow's MCR_Avg var
          double avg_mcr_tmp=0;
          for(int i=0;i<num_of_subch;i++)
          {
            avg_mcr_tmp+=tmp_relay_link_info.MCR_Subch.at(i);
          }
          tmp_relay_link_info.MCR_Avg = avg_mcr_tmp/num_of_subch;
        }
        // Mac/LWX env set_flow $src_nid $from_nid $to_nid $flow_info MCR_Subch <Subch Num> <MCR>
        // the unit of MCR is (bytes / (subch.*symbol))
        else
        {
          int subch_num_tmp =0;
          double MCR_tmp = 0;
          istringstream(argv[8]) >> subch_num_tmp;
          istringstream(argv[9]) >> MCR_tmp;
          
          if(MCR_tmp <0)
          {
            lwx_err_msg(argc, argv , 
              "error when setting relay flow in tcl, MCR_Subch should not be smaller than zero.");
          }
          
          tmp_relay_link_info.MCR_Subch.at(subch_num_tmp) = MCR_tmp;
          
          // calculate MCR_Avg and set to flow's MCR_Avg var
          double avg_mcr_tmp=0;
          for(int i=0;i<num_of_subch;i++)
          {
            avg_mcr_tmp+=tmp_relay_link_info.MCR_Subch.at(i);
          }
          tmp_relay_link_info.MCR_Avg = avg_mcr_tmp/num_of_subch;
        }
        // customize MCR_Subch
        
      }
      
      // put this relay info object into this flow's relay link vector
      Flow_Queue *tmp_flow = &tmp_node->ctrl_flow.at(flow_index);
      tmp_flow->relay_link.push_back(tmp_relay_link_info);
      return (TCL_OK);
    
  }
  /////////////////////////////
  // set or get env parameters end
  /////////////////////////////

  // error check start
  lwx_err_msg(argc, argv);
  // error check end
  
   return LWXClass::method(ac,av);
}
// allow user to access static var end

#endif

